EAP-TLS_Solution/EAP-TLS Client/lib/eap_tls_lib.c (1,698 lines of code) (raw):
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include "../applibs_versions.h"
#include <applibs/log.h>
#include <applibs/networking.h>
#include <applibs/wificonfig.h>
#include <applibs/storage.h>
#include <applibs/certstore.h>
#include <tlsutils/deviceauth_curl.h>
#include "../parson.h"
#include "../eventloop_timer_utilities.h"
#include "eap_tls_lib.h"
#include "web_api_client.h"
////////////////////////////////////////////////////////////////////////
// NOTE: search for '#NOTE' for incomplete implementations/highlights //
////////////////////////////////////////////////////////////////////////
/// <summary>
/// Globally accessible device configuration.
/// </summary>
DeviceConfiguration deviceConfiguration;
/// <summary>
/// State machine for the EapTls_RunConnectionManager API.
/// </summary>
typedef enum
{
ConnectionManagerState_TBD = -1, // Just a dead-end, to capture incomplete code branching
ConnectionManagerState_Idle = 0, // Just start-off: let's check if we have RootCA and Client certificates
ConnectionManagerState_CheckCertsInstalled, // Check if we have installed RootCA and Client certificates to use for connecting to the EAP_TLS network
ConnectionManagerState_Installcerts, // Install RootCA and Client certificates in the certificate store
ConnectionManagerState_Installcerts_Dup, // Install <new> RootCA and Client certificates in the certificate store, to be registered into the EAP_TLS's <TEMPORARY CLONED> network configuration
ConnectionManagerState_AddEapTlsNetwork, // Add the EAP_TLS network configuration from scratch (eventually removes existing)
ConnectionManagerState_CloneEapTlsNetwork, // Clone the EAP_TLS network configuration, for use in certificate renewal
ConnectionManagerState_ConfigureEapTlsNetwork, // Configure the EAP_TLS's network security (installing CA/Client certs)
ConnectionManagerState_ConfigureEapTlsNetwork_Dup, // Configure the EAP_TLS's <TEMPORARY CLONE> network security (installing CA/Client certs)
ConnectionManagerState_ConnectToEapTlsNetwork, // Attempt connecting to the EAP_TLS network
ConnectionManagerState_ConnectToEapTlsNetwork_Dup, // Attempt connecting to the EAP_TLS's <TEMPORARY CLONE> network
ConnectionManagerState_RequestCertificates, // Initiate a new RootCA and/or Client certificate request
ConnectionManagerState_ConnectToBootstrapNetwork, // Attempt connecting to a bootstrap network, in order to connect to the WebAPI and retrieve new certificates
ConnectionManagerState_CallMdmWebApi, // Request a new Client certificate to the WebAPI/CMS
ConnectionManagerState_HandleMdmWebApiResponse, // Handle the response from the WebAPI/CMS
ConnectionManagerState_SwapEapTlsNetworks, // Swap the original EAP_TLS network with the EAP_TLS's <TEMPORARY CLONE> network
ConnectionManagerState_Connected_Exit, // The device is connected to the EAP-TLS network -> the App can proceed its execution
ConnectionManagerState_Error_Exit // The device cannot connect to the EAP-TLS network -> let's return so the App can proceed its execution based on the return result
} ConnectionManagerState;
/// <summary>
/// Local structs & state-variables for the connection timer-handler
/// </summary>
static const struct timespec connectionPollSeconds = { .tv_sec = 10, .tv_nsec = 0 };
typedef struct
{
char connectionNetworkName[WIFICONFIG_CONFIG_NAME_MAX_LENGTH + 1];
EventLoop *eventLoop;
EventLoopTimer *connectionTimer;
volatile sig_atomic_t connectionRetries;
volatile sig_atomic_t exitCode;
} ConnectionTimerHandlerContext;
static ConnectionTimerHandlerContext connectionTimerHandlerContext =
{
"",
NULL,
NULL,
MAX_CONNECTION_RETRIES,
EapTlsResult_Error,
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// Logging utilities
//////////////////////////////////////////////////////////////
#define EAP_TLS_LIB_LOG_PREFIX "EAP-TLS lib: "
#define EapTls_Log(...) Log_Debug(EAP_TLS_LIB_LOG_PREFIX __VA_ARGS__)
//static void EapTls_Log(const char *fmt, ...)
//{
// va_list myargs;
// va_start(myargs, fmt);
//
// Log_Debug(EAP_TLS_LIB_LOG_PREFIX);
// Log_Debug(fmt, myargs);
//
// va_end(myargs);
//}
//////////////////////////////////////////////////////////////
// Timer handler for connecting to a network configuration
//////////////////////////////////////////////////////////////
static void ConnectionTimerEventHandler(EventLoopTimer *timer)
{
if (ConsumeEventLoopTimerEvent(timer) != 0)
{
connectionTimerHandlerContext.exitCode = EapTlsResult_Connecting;
return;
}
if (connectionTimerHandlerContext.connectionRetries--)
{
EapTlsResult iRes = EapTls_IsNetworkConnected(connectionTimerHandlerContext.connectionNetworkName);
if (EapTlsResult_Disconnected == iRes)
{
connectionTimerHandlerContext.exitCode = EapTlsResult_Connecting;
}
else
{
connectionTimerHandlerContext.exitCode = EapTlsResult_Connected;
}
}
else
{
connectionTimerHandlerContext.exitCode = EapTlsResult_ConnectionTimeout;
}
}
static EapTlsResult InitConnectionTimerHandler(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error;
// if not, what was the cause?
int srcNetworkID = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == srcNetworkID)
{
iRes = EapTlsResult_NetworkUnknown;
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
// Ok, we are now connected: we should wait for a timeout for the OS to scan and connect...
// Will make this blocking, as the App is expecting a deterministic result, and we don't have anything to do in the meanwhile.
if (NULL != connectionTimerHandlerContext.eventLoop)
{
EventLoop_Close(connectionTimerHandlerContext.eventLoop);
connectionTimerHandlerContext.eventLoop = NULL;
}
connectionTimerHandlerContext.eventLoop = EventLoop_Create();
if (NULL != connectionTimerHandlerContext.eventLoop)
{
connectionTimerHandlerContext.connectionRetries = MAX_CONNECTION_RETRIES;
strncpy(connectionTimerHandlerContext.connectionNetworkName, networkName, sizeof(connectionTimerHandlerContext.connectionNetworkName) - 1);
connectionTimerHandlerContext.exitCode = EapTlsResult_Connecting;
connectionTimerHandlerContext.connectionTimer = CreateEventLoopPeriodicTimer(connectionTimerHandlerContext.eventLoop, &ConnectionTimerEventHandler, &connectionPollSeconds);
if (NULL != connectionTimerHandlerContext.connectionTimer)
{
iRes = EapTlsResult_Success;
}
}
else
{
EapTls_Log("Could not create event loop.\n");
}
}
return iRes;
}
static void DisposeConnectionTimerHandler(void)
{
connectionTimerHandlerContext.exitCode = EapTlsResult_Error;
if (NULL != connectionTimerHandlerContext.connectionTimer)
{
DisposeEventLoopTimer(connectionTimerHandlerContext.connectionTimer);
connectionTimerHandlerContext.connectionTimer = NULL;
}
if (NULL != connectionTimerHandlerContext.eventLoop)
{
EventLoop_Close(connectionTimerHandlerContext.eventLoop);
connectionTimerHandlerContext.eventLoop = NULL;
}
}
//////////////////////////////////////////////////////////////
// Permanent device configuration helpers
//////////////////////////////////////////////////////////////
EapTlsResult EapTls_StoreDeviceConfiguration(const DeviceConfiguration *config)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != config)
{
// Note: if the DeviceConfiguration type is changed, the mutable file must be
// first deleted to store the new object size:
//
int fd = Storage_DeleteMutableFile();
if (-1 == fd)
{
EapTls_Log("No previous configuration mutable file to be deleted.\n");
}
else
{
EapTls_Log("Deleted previous configuration in mutable file.\n");
}
fd = Storage_OpenMutableFile();
if (-1 == fd)
{
EapTls_Log("ERROR: could not open mutable file: %s (%d).\n", strerror(errno), errno);
}
else
{
size_t written = 0;
while (written < sizeof(DeviceConfiguration))
{
ssize_t ret = write(fd, (unsigned char *)config + written, sizeof(DeviceConfiguration) - written);
if (-1 == ret)
{
EapTls_Log("ERROR: could write to mutable file: %s (%d).\n", strerror(errno), errno);
break;
}
else
{
written += (size_t)ret;
if (0 == ret)
{
EapTls_Log("FATAL: wrote 0 bytes to mutable file while still missing %d bytes -> aborting.\n", sizeof(DeviceConfiguration) - written);
break;
}
else if (written < sizeof(DeviceConfiguration))
{
EapTls_Log("Warning: only wrote %d of %d bytes requested, continuing...\n", written, sizeof(DeviceConfiguration));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Successfully persisted the device configuration in mutable file.\n");
break;
}
}
}
close(fd);
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_ReadDeviceConfiguration(DeviceConfiguration *configOut)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != configOut)
{
int fd = Storage_OpenMutableFile();
if (-1 != fd)
{
ssize_t ret = read(fd, configOut, sizeof(DeviceConfiguration));
if (-1 == ret)
{
EapTls_Log("ERROR: reading from mutable file: %s (%d).\n", strerror(errno), errno);
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Successfully read the device configuration from mutable file.\n");
}
close(fd);
}
else
{
EapTls_Log("ERROR: could not open mutable file: %s (%d).\n", strerror(errno), errno);
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
//////////////////////////////////////////////////////////////
// Network configuration helpers
//////////////////////////////////////////////////////////////
EapTlsResult EapTls_PersistNetworkConfig(void)
{
EapTlsResult iRes = EapTlsResult_Error;
if (WifiConfig_PersistConfig() == -1)
{
EapTls_Log("Cannot persist eapTlsConfig: errno=%d (%s)\n", errno, strerror(errno));
}
else
{
if (WifiConfig_ReloadConfig() == -1)
{
EapTls_Log("Cannot reload eapTlsConfig: errno=%d (%s)\n", errno, strerror(errno));
}
else
{
EapTls_Log("Successfully persisted & reloaded network configurations!\n");
iRes = EapTlsResult_Success;
}
}
return iRes;
}
EapTlsResult EapTls_DiagnoseNetwork(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
int networkID = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == networkID)
{
iRes = EapTlsResult_NetworkUnknown;
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
WifiConfig_NetworkDiagnostics networkDiagnostics;
int res = WifiConfig_GetNetworkDiagnostics(networkID, &networkDiagnostics);
if (-1 == res)
{
iRes = EapTlsResult_FailedDiagnosingNetwork;
EapTls_Log("Failed getting diagnostics for network '%s' - Id[%d]: errno=%d (%s)\n", networkName, networkID, errno, strerror(errno));
}
else
{
// Check the connection state, and report eventual errors
if (networkDiagnostics.isConnected)
{
iRes = EapTlsResult_Connected;
}
else
{
switch (networkDiagnostics.error)
{
case 2: // NetworkNotFound = 2 : Network was not found.
{
iRes = EapTlsResult_NetworkUnknown;
EapTls_Log("Network '%s' - Id[%d] not found!\n", networkName, networkID);
}
break;
case 5: // AuthenticationFailed = 5: Authentication failed. This error is thrown for EAP-TLS
{
// Let's attempt requesting new certificates (we already validated the certs at the first state)
switch (networkDiagnostics.certError)
{
case 101: // InvalidRootCA
{
iRes = EapTlsResult_AuthenticationError_InvalidRootCaCert;
}
break;
case 102: // InvalidClientAuth
{
iRes = EapTlsResult_AuthenticationError_InvalidClientCert;
}
break;
case 103: // UnknownClientId
{
iRes = EapTlsResult_AuthenticationError_InvalidClientIdentity;
}
break;
default:
{
iRes = EapTlsResult_AuthenticationError;
}
break;
}
EapTls_Log("Authentication error connecting to network '%s' - Id[%d]: error=%d, certError=%d\n", networkName, networkID, networkDiagnostics.error, networkDiagnostics.certError);
}
break;
case 1: // ConnectionFailed = 1 : Generic error message when connection fails.
case 3: // NoPskIncluded = 3: Network password is missing.
case 4: // WrongKey = 4: Network is using an incorrect password.
case 6: // SecurityTypeMismatch = 6: The stored network's security type does not match the available network.
case 7: // NetworkFrequencyNotAllowed = 7: Network frequency not allowed.
case 8: // NetworkNotEssPbssMbss = 8: Network is not supported because no ESS, PBSS or MBSS was detected.
case 9: // NetworkNotSupported = 9: Network is not supported.
case 10: // NetworkNonWpa = 10: Network is not WPA2PSK, WPA2EAP or Open.
{
iRes = EapTlsResult_ConnectionError;
EapTls_Log("FAILED connecting to network '%s' - Id[%d]: error=%d\n", networkName, networkID, networkDiagnostics.error);
}
break;
default:
{
EapTls_Log("ERROR connecting to network '%s' - Id[%d]: error=%d\n", networkName, networkID, networkDiagnostics.error);
if (!networkDiagnostics.isEnabled)
{
iRes = EapTlsResult_NetworkDisabled;
}
else
{
// This should never happen!
iRes = EapTlsResult_BadParameters;
}
}
break;
}
}
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_SetTargetScanOnNetwork(const char *networkName, bool enabled)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
int targetNetworkId = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == targetNetworkId)
{
iRes = EapTlsResult_NetworkUnknown;
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
// First, we want to leave the current connected network as not target-scanned
int currentNetworkId = WifiConfig_GetConnectedNetworkId();
if (-1 == currentNetworkId)
{
EapTls_Log("Failed retrieving current connected network Id: errno=%d (%s)\n", errno, strerror(errno));
}
else if (currentNetworkId != targetNetworkId)
{
EapTls_Log("Currently connected to network Id [%d]: disabling target scan on this network...\n", currentNetworkId);
if (-1 == WifiConfig_SetTargetedScanEnabled(currentNetworkId, false))
{
EapTls_Log("Cannot reset targeted scan for network Id[%d]: errno=%d (%s)\n", currentNetworkId, errno, strerror(errno));
}
}
EapTls_Log("Found target network configuration '%s' @ id=%d\n", networkName, targetNetworkId);
if (-1 == WifiConfig_SetTargetedScanEnabled(targetNetworkId, enabled))
{
iRes = EapTlsResult_FailedScanningNetwork;
EapTls_Log("Cannot set targeted scan for network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
// The setting is effective immediately but won't persist across device reboots unless the WifiConfig_PersistConfig function is called after this function.
// NOTE: Targeted scanning causes the device to transmit probe requests that may reveal the SSID of the network to other devices.
// This should only be used in controlled environments, or on networks where this an acceptable risk.
EapTls_Log("Successfully set target scanning for network configuration '%s' @ id=%d\n", networkName, targetNetworkId);
int count = WifiConfig_TriggerScanAndGetScannedNetworkCount(); // blocking call
if (-1 == count)
{
iRes = EapTlsResult_FailedScanningNetwork;
EapTls_Log("Cannot trigger scan for network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Triggered scan and successfully found %d networks.\n", count);
}
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_IsNetworkIdConnected(int networkID)
{
EapTlsResult iRes = EapTlsResult_Error;
int connNetworkID = WifiConfig_GetConnectedNetworkId();
if (-1 == connNetworkID)
{
//if (ENOTCONN == connNetworkID)
{
iRes = EapTlsResult_Disconnected;
}
EapTls_Log("Not connected to any network: errno=%d (%s)\n", errno, strerror(errno));
}
else
{
iRes = (connNetworkID == networkID) ? EapTlsResult_Connected : EapTlsResult_Disconnected;
}
return iRes;
}
EapTlsResult EapTls_IsNetworkConnected(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
int networkID = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == networkID)
{
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
iRes = EapTlsResult_NetworkUnknown;
}
else
{
iRes = EapTls_IsNetworkIdConnected(networkID);
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_DisableAllNetworksExcept(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
int targetNetworkId = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == targetNetworkId)
{
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
iRes = EapTlsResult_NetworkUnknown;
}
else
{
// #NOTE: At the moment, this is implemented with a workaround, since
// the WifiConfig_StoredNetwork struct misses struct member to identify the related network.
for (int networkId = 0; networkId < MAX_NETWORK_CONFIGURATIONS; networkId++)
{
int res = WifiConfig_SetNetworkEnabled(networkId, (networkId == targetNetworkId) ? true : false);
if (-1 == res)
{
// #NOTE: disabled since it's a workaround
//iRes = EapTlsResult_Error;
EapTls_Log("Cannot %s network configuration ID[%d]: errno=%d (%s)\n", (networkId == targetNetworkId) ? "enable" : "disable", networkId, errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Successfully %s network configuration ID[%d]\n", (networkId == targetNetworkId) ? "enabled" : "disabled", networkId);
}
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_SetNetworkEnabledState(const char *networkName, bool enabled)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
int targetNetworkId = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == targetNetworkId)
{
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
iRes = EapTlsResult_NetworkUnknown;
}
else
{
if (-1 == WifiConfig_SetNetworkEnabled(targetNetworkId, enabled))
{
EapTls_Log("Cannot %s network configuration '%s': errno=%d (%s)\n", enabled ? "enable" : "disable", errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Network configuration '%s' is now <%s>\n", networkName, enabled ? "enabled" : "disabled");
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_AddNetwork(const char *networkName, const char *networkSSID, WifiConfig_Security_Type securityType, const char *psk)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName && NULL != networkSSID)
{
int networkID = WifiConfig_AddNetwork();
if (-1 == networkID)
{
EapTls_Log("Cannot add a new network configuration: errno=%d (%s)\n", errno, strerror(errno));
}
else
{
EapTls_Log("Configuring a new '%s' network with security type [%d]...\n", networkName, securityType);
if (-1 == WifiConfig_SetConfigName(networkID, networkName))
{
EapTls_Log("Cannot set eapTlsConfig name %s: errno=%d (%s)\n", networkSSID, errno, strerror(errno));
return EapTlsResult_Error;
}
if (-1 == WifiConfig_SetSSID(networkID, networkSSID, strlen(networkSSID)))
{
EapTls_Log("Cannot set SSID %s: errno=%d (%s)\n", networkSSID, errno, strerror(errno));
return EapTlsResult_Error;
}
if (-1 == WifiConfig_SetSecurityType(networkID, securityType))
{
EapTls_Log("Cannot set eapTlsConfig type to %d: errno=%d (%s)\n", securityType, errno, strerror(errno));
return EapTlsResult_Error;
}
if (NULL != psk)
{
if (-1 == WifiConfig_SetPSK(networkID, psk, strnlen(psk, WIFICONFIG_WPA2_KEY_MAX_BUFFER_SIZE)))
{
EapTls_Log("Cannot set PSK: errno=%d (%s)\n", securityType, errno, strerror(errno));
return EapTlsResult_Error;
}
}
if (-1 == WifiConfig_SetNetworkEnabled(networkID, true))
{
EapTls_Log("Cannot enable network %s: errno=%d (%s)\n", networkSSID, errno, strerror(errno));
return EapTlsResult_Error;
}
iRes = EapTls_PersistNetworkConfig();
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_RemoveNetwork(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
EapTls_Log("Looking for existing '%s' network configuration...\n", networkName);
int srcNetworkID = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == srcNetworkID)
{
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
EapTls_Log("Forgetting previous '%s' network...\n", networkName);
if (-1 == WifiConfig_ForgetNetworkById(srcNetworkID))
{
EapTls_Log("Cannot forget eapTlsConfig: errno=%d (%s)\n", errno, strerror(errno));
}
else
{
iRes = EapTls_PersistNetworkConfig();
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_CloneNetworkConfig(EapTlsConfig *srcNetwork, EapTlsConfig *dstNetwork)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != srcNetwork && NULL != dstNetwork)
{
int srcNetworkID = WifiConfig_GetNetworkIdByConfigName(srcNetwork->eapTlsNetworkName);
if (-1 == srcNetworkID)
{
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", srcNetwork->eapTlsNetworkName, errno, strerror(errno));
}
else
{
int newNetworkID = WifiConfig_AddDuplicateNetwork(srcNetworkID, dstNetwork->eapTlsNetworkName);
if (-1 == newNetworkID)
{
EapTls_Log("Cannot duplicate eapTlsConfig name '%s': errno=%d (%s)\n", dstNetwork->eapTlsNetworkName, errno, strerror(errno));
}
else
{
if (-1 == WifiConfig_SetSSID(newNetworkID, dstNetwork->eapTlsNetworkSsid, strlen(dstNetwork->eapTlsNetworkSsid)))
{
EapTls_Log("Cannot set SSID %s: errno=%d (%s)\n", dstNetwork->eapTlsNetworkName, errno, strerror(errno));
}
else
{
return EapTlsResult_Success;
}
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_ConfigureNetworkSecurity(const char *networkName, const char *identity, const char *rootCACertificateId, const char *clientCertificateId)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName && NULL != identity && NULL != rootCACertificateId && NULL != clientCertificateId)
{
int networkID = WifiConfig_GetNetworkIdByConfigName(networkName);
if (-1 == networkID)
{
EapTls_Log("Cannot find network configuration '%s': errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else
{
EapTls_Log("Configuring security for the '%s' EAP-TLS network...\n", networkName);
if (WifiConfig_SetClientIdentity(networkID, identity) == -1)
{
EapTls_Log("Cannot set client identity %s: errno=%d (%s)\n", networkName, errno, strerror(errno));
}
else if (WifiConfig_SetRootCACertStoreIdentifier(networkID, rootCACertificateId) == -1)
{
EapTls_Log("Cannot set RootCA %s: errno=%d (%s)\n", rootCACertificateId, errno, strerror(errno));
}
else if (WifiConfig_SetClientCertStoreIdentifier(networkID, clientCertificateId) == -1)
{
EapTls_Log("Cannot set client certificate to network %s: errno=%d (%s)\n", clientCertificateId, errno, strerror(errno));
}
else if (EapTls_PersistNetworkConfig() == EapTlsResult_Error)
{
EapTls_Log("Cannot persist eapTlsConfig: errno=%d\n", iRes);
}
else
{
EapTls_Log("Successfully configured '%s' EAP-TLS network!\n", networkName);
iRes = EapTlsResult_Success;
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_WaitToConnectTo(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error, retries = MAX_CONNECTION_RETRIES;
const struct timespec sleepTime = { .tv_sec = 10, .tv_nsec = 0 };
if (NULL != networkName)
{
while (retries--)
{
EapTls_Log("Connection attempt #%d...\n", MAX_CONNECTION_RETRIES - retries);
iRes = EapTls_IsNetworkConnected(networkName);
if (EapTlsResult_Connected == iRes)
{
EapTls_Log("Successfully connected to network '%s'\n", networkName);
return iRes;
}
else if (EapTlsResult_NetworkUnknown == iRes)
{
return iRes;
}
nanosleep(&sleepTime, NULL);
}
iRes = EapTlsResult_ConnectionTimeout;
EapTls_Log("TIMEOUT connecting to network '%s'\n", networkName);
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_ConnectToNetwork(const char *networkName)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != networkName)
{
// Let's set the device to connect to the specific network that was asked
{
// Ideally these calls should be used (but both implementing workarounds!):
iRes = EapTls_DisableAllNetworksExcept(networkName);
// or
//iRes = EapTls_SetTargetScanOnNetwork(networkName, true);
}
if (EapTlsResult_Success == iRes)
{
// Let's kick-off the connection handler
iRes = InitConnectionTimerHandler(networkName);
if (EapTlsResult_Success == iRes)
{
iRes = EapTlsResult_Connecting;
EapTls_Log("Initialized connection handler to network '%s'\n", networkName);
// Use event loop to wait for events and trigger handlers, until an error or SIGTERM happens
while (connectionTimerHandlerContext.exitCode == EapTlsResult_Connecting)
{
EventLoop_Run_Result result = EventLoop_Run(connectionTimerHandlerContext.eventLoop, -1, true);
// Continue if interrupted by signal, e.g. due to breakpoint being set.
if (result == EventLoop_Run_Failed && errno != EINTR)
{
iRes = EapTlsResult_Error;
goto exit_timer;
}
iRes = (EapTlsResult)connectionTimerHandlerContext.exitCode;
switch (iRes)
{
case EapTlsResult_Connecting:
{
EapTls_Log("Attempt #%d connecting to network '%s'... \n", MAX_CONNECTION_RETRIES - connectionTimerHandlerContext.connectionRetries, networkName);
}
break;
case EapTlsResult_Connected:
{
EapTls_Log("CONNECTED to network '%s'!\n", networkName);
goto exit_timer;
}
break;
case EapTlsResult_ConnectionTimeout:
{
EapTls_Log("Timeout connecting to network '%s'\n", networkName);
iRes = EapTls_DiagnoseNetwork(connectionTimerHandlerContext.connectionNetworkName);
if (EapTlsResult_FailedDiagnosingNetwork == iRes)
{
// Let's tweak this result translating it to a timeout error
iRes = EapTlsResult_ConnectionTimeout;
}
goto exit_timer;
}
break;
default:
EapTls_Log("EapTls_ConnectToNetwork UNHANDLED CASE!!!!\n", networkName);
break;
}
}
}
else
{
iRes = EapTlsResult_Error;
EapTls_Log("FAILED to initialize connection handler to network '%s'\n", networkName);
}
}
else
{
EapTls_Log("FAILED to targeting connection to network '%s'\n", networkName);
}
}
else
{
iRes = EapTlsResult_BadParameters;
}
return iRes;
exit_timer:
DisposeConnectionTimerHandler();
return iRes;
}
//////////////////////////////////////////////////////////////
// Certificate store helpers
//////////////////////////////////////////////////////////////
EapTlsResult EapTls_CompareCertificates(const char *certificateId, const uint8_t *certificatePem, size_t certSize)
{
EapTlsResult iRes = 0;
if (NULL != certificateId && *certificateId && NULL != certificatePem && *certificatePem)
{
#if USE_ADDITIONAL_API_STUBS
// MISSING APIs!!
CertStore_Fingerprint fp1, fp2;
int res = CertStore_GetCertificateFingerprint(certificateId, &fp1);
if (-1 != res)
{
res = CertStore_GetBlobCertificateFingerprint(certificatePem, certSize, &fp2);
if (-1 != iRes)
{
iRes = memcmp(&fp1, &fp2, sizeof(CertStore_Fingerprint)) == 0 ? EapTlsResult_SUCCESS : EapTlsResult_ERROR;
}
}
#else
// There is no released API to retrieve the fingerprint on a stored certificate and neither on a PEM blob, so we just say they are different...
// NOTE!! this uses flash write-cycles to (re)write certificate!!
iRes = EapTlsResult_Error;
#endif
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("ERROR - Invalid certificate Id!\n");
}
return iRes;
}
EapTlsResult EapTls_ValidateCertificates(const char *certificateId, const char *expectedSubject, const char *expectedIssuer)
{
EapTlsResult iRes = EapTlsResult_Error;
struct tm tmNotBefore = { 0 }, tmNotAfter = { 0 };
CertStore_SubjectName outSubjectName;
CertStore_IssuerName outIssuerName;
if (NULL != certificateId && *certificateId)
{
if (CertStore_GetCertificateNotBefore(certificateId, &tmNotBefore) == 0)
{
if (CertStore_GetCertificateNotAfter(certificateId, &tmNotAfter) == 0)
{
if (CertStore_GetCertificateSubjectName(certificateId, &outSubjectName) == 0)
{
if (CertStore_GetCertificateIssuerName(certificateId, &outIssuerName) == 0)
{
time_t now = time(0);
time_t tNotBefore = mktime(&tmNotBefore);
time_t tNotAfter = mktime(&tmNotAfter);
//
// #NOTE: validate dates, etc. as per your custom requirements
//
if (difftime(now, tNotAfter) > 0) // If positive, then now > t_not_after
{
EapTls_Log("Certificate '%s' is expired!\n", certificateId);
}
else if (difftime(tNotBefore, now) > 0)
{
EapTls_Log("Certificate '%s' is not yet valid!\n", certificateId);
}
else if (NULL != expectedSubject && 0 != strcmp(outSubjectName.name, expectedSubject))
{
EapTls_Log("Certificate '%s' doesn't have the expected Subject (%s/%s)!\n", outSubjectName.name, expectedSubject);
}
else if (NULL != expectedIssuer && 0 != strcmp(outIssuerName.name, expectedIssuer))
{
EapTls_Log("Certificate '%s' doesn't have the expected Issuer (%s/%s)!\n", outIssuerName.name, expectedIssuer);
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Certificate '%s' is valid.\n", certificateId);
}
}
else
{
EapTls_Log("ERROR retrieving the IssuerName for certificate Id '%s': errno=%d (%s)\n", certificateId, errno, strerror(errno));
}
}
else
{
EapTls_Log("ERROR retrieving the SubjectName for certificate Id '%s': errno=%d (%s)\n", certificateId, errno, strerror(errno));
}
}
else
{
EapTls_Log("ERROR retrieving the 'Not After' date for certificate Id '%s': errno=%d (%s)\n", certificateId, errno, strerror(errno));
}
}
else
{
EapTls_Log("ERROR retrieving the 'Not Before' date for certificate Id '%s': errno=%d (%s)\n", certificateId, errno, strerror(errno));
}
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("ERROR - Invalid certificate Id!\n");
}
return iRes;
}
EapTlsResult EapTls_IsCertificateInstalled(const char *certificateId)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != certificateId && *certificateId)
{
int certCount = CertStore_GetCertificateCount();
if (-1 == certCount)
{
EapTls_Log("ERROR counting certificates in the CertStore: errno=%d (%s)\n", errno, strerror(errno));
}
else
{
for (int index = 0; index < certCount; index++)
{
CertStore_Identifier id;
if (-1 == CertStore_GetCertificateIdentifierAt((size_t)index, &id))
{
EapTls_Log("ERROR finding certificate '%s' in the CertStore: errno=%d (%s)\n", certificateId, errno, strerror(errno));
}
else
{
if (0 == strncmp(id.identifier, certificateId, strlen(id.identifier)))
{
EapTls_Log("Certificate '%s' is installed in the CertStore\n", certificateId);
return EapTlsResult_Success;
}
}
}
EapTls_Log("Certificate '%s' is NOT installed in the CertStore\n", certificateId);
}
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("ERROR - Invalid certificate Id!\n");
}
return iRes;
}
EapTlsResult EapTls_CheckcertStoreFreeSpace(size_t certificateSize)
{
EapTlsResult iRes = EapTlsResult_Success;
ssize_t availableSpace = CertStore_GetAvailableSpace();
if (-1 == availableSpace)
{
iRes = EapTlsResult_Error;
EapTls_Log("ERROR: CertStore_GetAvailableSpace has failed: errno=%d (%s)\n", errno, strerror(errno));
}
else if (((size_t)availableSpace) < certificateSize)
{
iRes = EapTlsResult_Error;
EapTls_Log("ERROR: Available space (%zu) is less than the required space: (%zu).\n", availableSpace, certificateSize);
}
return iRes;
}
EapTlsResult EapTls_InstallRootCaCertificatePem(const char *certificateId, const char *certificatePem)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != certificateId &&
NULL != certificateId && *certificateId &&
NULL != certificatePem && *certificatePem)
{
size_t cert_len = strlen(certificatePem);
if (EapTls_CheckcertStoreFreeSpace(cert_len) == EapTlsResult_Success)
{
if (CertStore_InstallRootCACertificate(certificateId, certificatePem, cert_len) == 0)
{
iRes = EapTlsResult_Success;
EapTls_Log("Successfully installed '%s' rootCA certificate\n", certificateId);
}
else
{
EapTls_Log("Error installing the CA certificate: errno=%d (%s)\n", errno, strerror(errno));
}
}
else
{
iRes = EapTlsResult_CertStoreFull;
EapTls_Log("Error: not enough space left in the CertStore\n");
}
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("Invalid parameters!\n");
}
return iRes;
}
EapTlsResult EapTls_InstallRootCaCertificate(Certificate *certificate)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != certificate && *certificate->relativePath)
{
EapTls_Log("Looking for rootCA certificate @ '%s'...\n", certificate->relativePath);
int fd = Storage_OpenFileInImagePackage(certificate->relativePath);
if (-1 != fd)
{
char *certificateBuffer = malloc(CERTSTORE_MAX_CERT_SIZE);
if (NULL != certificateBuffer)
{
if (read(fd, certificateBuffer, CERTSTORE_MAX_CERT_SIZE) >= 0)
{
// Install the RootCA certificate
iRes = EapTls_InstallRootCaCertificatePem(certificate->id, certificateBuffer);
}
free(certificateBuffer);
}
else
{
iRes = EapTlsResult_OutOfMemory;
Log_Debug("Out of memory in loading rootCA certificate @ '%s'...\n", certificate->relativePath);
}
close(fd);
}
else
{
EapTls_Log("The certificate file could not be read: errno=%d (%s)\n", errno, strerror(errno));
}
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("Invalid parameters!\n");
}
return iRes;
}
EapTlsResult EapTls_InstallClientCertificatePem(const char *certificateId, const char *certificatePem, const char *privateKeyPem, const char *privateKeyPassword)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != certificateId && *certificateId &&
NULL != certificatePem && *certificatePem)
{
size_t cert_len = strlen(certificatePem), pk_len = strlen(privateKeyPem);
if (EapTls_CheckcertStoreFreeSpace(cert_len + pk_len) == EapTlsResult_Success)
{
// Install the Client certificate
if (CertStore_InstallClientCertificate(certificateId, certificatePem, cert_len, privateKeyPem, pk_len, privateKeyPassword) == 0)
{
iRes = EapTlsResult_Success;
EapTls_Log("Successfully installed '%s' client certificate\n", certificateId);
}
else
{
EapTls_Log("Error installing the client certificate: errno=%d (%s)\n", errno, strerror(errno));
}
}
else
{
iRes = EapTlsResult_CertStoreFull;
EapTls_Log("Error: not enough space left in the CertStore\n");
}
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("Invalid parameters!\n");
}
return iRes;
}
EapTlsResult EapTls_InstallClientCertificate(Certificate *certificate)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != certificate &&
*certificate->id &&
*certificate->relativePath &&
*certificate->privateKeyRelativePath)
{
EapTls_Log("Looking for Client certificate @ '%s'...\n", certificate->relativePath);
int fd = Storage_OpenFileInImagePackage(certificate->relativePath);
if (-1 != fd)
{
char *certificateBuffer = malloc(CERTSTORE_MAX_CERT_SIZE);
if (NULL != certificateBuffer)
{
if (read(fd, certificateBuffer, CERTSTORE_MAX_CERT_SIZE) >= 0)
{
EapTls_Log("Looking for private key @ '%s'...\n", certificate->privateKeyRelativePath);
int fdpk = Storage_OpenFileInImagePackage(certificate->privateKeyRelativePath);
if (-1 != fd)
{
char *privateKeyBuffer = malloc(CERTSTORE_MAX_CERT_SIZE);
if (NULL != privateKeyBuffer)
{
if (read(fdpk, privateKeyBuffer, CERTSTORE_MAX_CERT_SIZE) >= 0)
{
// Install the Client certificate
iRes = EapTls_InstallClientCertificatePem(certificate->id, certificateBuffer, privateKeyBuffer, certificate->privateKeyPass);
}
else
{
EapTls_Log("The private key file could not be read: errno=%d (%s)\n", errno, strerror(errno));
}
free(privateKeyBuffer);
}
else
{
iRes = EapTlsResult_OutOfMemory;
Log_Debug("Out of memory in loading client certificate @ '%s'...\n", certificate->privateKeyRelativePath);
}
close(fdpk);
}
else
{
EapTls_Log("The certificate's private key file could not be read: errno=%d (%s)\n", errno, strerror(errno));
}
close(fd);
}
else
{
EapTls_Log("The certificate file could not be read: errno=%d (%s)\n", errno, strerror(errno));
}
free(certificateBuffer);
}
else
{
iRes = EapTlsResult_OutOfMemory;
Log_Debug("Out of memory in loading rooclienttCA certificate @ '%s'...\n", certificate->relativePath);
}
}
}
else
{
iRes = EapTlsResult_BadParameters;
EapTls_Log("Invalid parameters!\n");
}
return iRes;
}
//////////////////////////////////////////////////////////////
// EAP-TLS network management
//////////////////////////////////////////////////////////////
EapTlsResult EapTls_SetBootstrapNetworkInterfaceType(EapTlsConfig *eapTlsConfig, NetworkInterfaceType networkInterfaceType)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != eapTlsConfig)
{
switch (networkInterfaceType)
{
case NetworkInterfaceType_Wifi:
{
eapTlsConfig->bootstrapNetworkInterfaceType = NetworkInterfaceType_Wifi;
int res = Networking_SetInterfaceState(NET_INTERFACE_WLAN0, true);
if (-1 == res)
{
EapTls_Log("Error setting interface state to '%s': %d(%s)\n", NET_INTERFACE_WLAN0, errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Bootstrap network is set to '%s'.\n", NET_INTERFACE_WLAN0);
// If the bootstrap network is on Wi-Fi, then disable the Ethernet interface
res = Networking_SetInterfaceState(NET_INTERFACE_ETHERNET0, false);
if (-1 == res)
{
EapTls_Log("Error disabling '%s': %d(%s)\n", NET_INTERFACE_ETHERNET0, errno, strerror(errno));
}
}
}
break;
case NetworkInterfaceType_Ethernet:
{
eapTlsConfig->bootstrapNetworkInterfaceType = NetworkInterfaceType_Ethernet;
int res = Networking_SetInterfaceState(NET_INTERFACE_ETHERNET0, true);
if (-1 == res)
{
EapTls_Log("Error setting interface state to '%s': %d(%s)\n", NET_INTERFACE_ETHERNET0, errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Bootstrap network is set to '%s'.\n", NET_INTERFACE_ETHERNET0);
}
}
break;
default:
{
eapTlsConfig->bootstrapNetworkInterfaceType = NetworkInterfaceType_Undefined;
EapTls_Log("ERROR: unknown interface type [%d]!\n", networkInterfaceType);
}
break;
}
}
else
{
EapTls_Log("ERROR: EAP-TLS configuration is NULL!");
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_SetBootstrapNetworkEnabledState(EapTlsConfig* eapTlsConfig, bool enabled)
{
EapTlsResult iRes = EapTlsResult_Error;
if (NULL != eapTlsConfig)
{
if (NetworkInterfaceType_Wifi == eapTlsConfig->bootstrapNetworkInterfaceType)
{
// On Wi-Fi, we just "disable" the bootstrap configuration, not the whole interface,
// as that would prevent connecting to the RADIUS network over Wi-Fi
iRes = EapTls_SetNetworkEnabledState(eapTlsConfig->bootstrapNetworkName, enabled);
if (EapTlsResult_Success == iRes)
{
EapTls_Log("Bootstrap network on '%s' is now <%s>\n", NET_INTERFACE_WLAN0, enabled ? "enabled" : "disabled");
}
}
else
{
// On Wi-Fi, we disable the whole interface, as this would not prevent connecting to the RADIUS network over Wi-Fi
int err = Networking_SetInterfaceState(NET_INTERFACE_ETHERNET0, enabled);
if (err == -1)
{
Log_Debug("Error setting interface state of '%s': %d(%s)\n", NET_INTERFACE_ETHERNET0, errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Success;
EapTls_Log("Bootstrap network on '%s' is now <%s>\n", NET_INTERFACE_ETHERNET0, enabled ? "enabled" : "disabled");
}
}
}
else
{
EapTls_Log("ERROR: EAP-TLS configuration is NULL!");
iRes = EapTlsResult_BadParameters;
}
return iRes;
}
EapTlsResult EapTls_RunConnectionManager(EapTlsConfig *eapTlsConfig)
{
EapTlsResult iRes = EapTlsResult_Error;
// Global variables (to state-machine)
EapTlsConfig radiusNetwork;
EapTlsConfig radiusNetwork_dup;
bool requestRootCaCertificate;
bool requestClientCertificate;
// Variable for handling the EAP-TLS network configuration, including its duplicate (used when renewing certificates with the WebAPI).
bool duplicatingNetwork = false;
// Temporary storage for the RootCA & Client certificates returned from the WebAPI (use Stack or static depending on SRAM/Flash constraints)
MemoryBlock webApiResponseBlob = { .data = NULL, .size = 0 };
WebApiResponse *webApiResponse = NULL;
// Check basic parameter requirements
{
if (NULL == eapTlsConfig)
{
EapTls_Log("Invalid configuration pointer.\n");
return EapTlsResult_BadParameters;
}
if (NetworkInterfaceType_Undefined == eapTlsConfig->bootstrapNetworkInterfaceType)
{
EapTls_Log("ERROR: EapTlsConfig::bootstrapNetworkInterfaceType is undefined!\n");
return EapTlsResult_BadParameters;
}
if (NULL == eapTlsConfig->bootstrapNetworkName || 0 == eapTlsConfig->bootstrapNetworkName)
{
EapTls_Log("ERROR: EapTlsConfig::bootstrapNetworkName is NULL or empty!\n");
return EapTlsResult_BadParameters;
}
if (NULL == eapTlsConfig->bootstrapNetworkSsid || 0 == eapTlsConfig->bootstrapNetworkSsid)
{
EapTls_Log("ERROR: EapTlsConfig::bootstrapNetworkSsid is NULL or empty!\n");
return EapTlsResult_BadParameters;
}
if (NULL == eapTlsConfig->mdmWebApiInterfaceUrl || 0 == eapTlsConfig->mdmWebApiInterfaceUrl)
{
EapTls_Log("ERROR: EapTlsConfig::mdmWebApiInterfaceUrl is NULL or empty!\n");
return EapTlsResult_BadParameters;
}
if (NULL == eapTlsConfig->eapTlsRootCertificate.id || 0 == eapTlsConfig->eapTlsRootCertificate.id)
{
EapTls_Log("ERROR: EapTlsConfig::eapTlsRootCertificate.id is NULL or empty!\n");
return EapTlsResult_BadParameters;
}
if (NULL == eapTlsConfig->eapTlsClientCertificate.id || 0 == eapTlsConfig->eapTlsClientCertificate.id)
{
EapTls_Log("ERROR: EapTlsConfig::eapTlsClientCertificate.id is NULL or empty!\n");
return EapTlsResult_BadParameters;
}
if (NULL == eapTlsConfig->eapTlsClientCertificate.privateKeyPass || 0 == eapTlsConfig->eapTlsClientCertificate.privateKeyPass)
{
EapTls_Log("ERROR: EapTlsConfig::eapTlsClientCertificate.privateKeyPass is NULL or empty!\n");
return EapTlsResult_BadParameters;
}
}
// Clone the configurations to the library's statics
memcpy(&radiusNetwork, eapTlsConfig, sizeof(EapTlsConfig));
memcpy(&radiusNetwork_dup, eapTlsConfig, sizeof(EapTlsConfig));
bool exitStateMachine = false;
ConnectionManagerState currentState = ConnectionManagerState_Idle;
while (!exitStateMachine)
{
switch (currentState)
{
case ConnectionManagerState_TBD: // Just a dead-end, to capture incomplete branch coding
{
EapTls_Log("Stuck into EAP_TLS_TBD... see call-stack!!\n");
while (1);
}
break;
case ConnectionManagerState_Idle: // Just starting off: let's check if we have RootCA and Client certificates
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_Idle\n");
currentState = ConnectionManagerState_CheckCertsInstalled;
}
break;
case ConnectionManagerState_CheckCertsInstalled: // Check if we have installed RootCA and Client certificates
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_CheckCertsInstalled\n");
requestRootCaCertificate = (EapTlsResult_Success != EapTls_IsCertificateInstalled(radiusNetwork.eapTlsRootCertificate.id));
requestClientCertificate = (EapTlsResult_Success != EapTls_IsCertificateInstalled(radiusNetwork.eapTlsClientCertificate.id));
currentState = (requestRootCaCertificate || requestClientCertificate) ? ConnectionManagerState_RequestCertificates : ConnectionManagerState_ConnectToEapTlsNetwork;
}
break;
case ConnectionManagerState_AddEapTlsNetwork: // Add the EAP_TLS network from scratch (eventually removes existing)
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_AddEapTlsNetwork\n");
// Always (eventually) remove the EAP_TLS network (no return/error check required, logging already within the API)
EapTls_RemoveNetwork(radiusNetwork.eapTlsNetworkName);
// Let's check if we can immediately connect to the EAP_TLS server
iRes = EapTls_AddNetwork(radiusNetwork.eapTlsNetworkName, radiusNetwork.eapTlsNetworkSsid, WifiConfig_Security_Wpa2_EAP_TLS, NULL);
if (EapTlsResult_Success == iRes)
{
EapTls_Log("Successfully added new EAP-TLS network '%s'!\n", radiusNetwork.eapTlsNetworkName);
currentState = ConnectionManagerState_ConfigureEapTlsNetwork;
continue;
}
// We cannot add the network --> let's give back control to the app to decide
iRes = EapTlsResult_FailedAddingEapTlsNetwork;
currentState = ConnectionManagerState_Error_Exit;
}
break;
case ConnectionManagerState_CloneEapTlsNetwork: // Clone the EAP_TLS network, for use in certificate renewal
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_CloneEapTlsNetwork\n");
// Let's duplicate the configuration structures, except for the network name and certificate IDs
radiusNetwork_dup = radiusNetwork;
snprintf(radiusNetwork_dup.eapTlsNetworkName, sizeof(radiusNetwork_dup.eapTlsNetworkName) - 1, "_%s", radiusNetwork.eapTlsNetworkName);
// Always (eventually) remove the old attempts of duplication of the EAP_TLS network (no return/error check required, logging already within the API)
EapTls_RemoveNetwork(radiusNetwork_dup.eapTlsNetworkName);
iRes = EapTls_CloneNetworkConfig(&radiusNetwork, &radiusNetwork_dup);
if (EapTlsResult_Success != iRes)
{
EapTls_Log("Cannot create temporary network configuration for '%s'\n", radiusNetwork.eapTlsNetworkName);
// We cannot attempt duplicating the network to try a different authentication path --> let's give back control to the app to decide
iRes = EapTlsResult_FailedCloningEapTlsNetwork;
currentState = ConnectionManagerState_Error_Exit;
}
else
{
// Let's install the certs only once we are sure we'll have a network configuration to attach them to
currentState = ConnectionManagerState_Installcerts_Dup;
}
}
break;
// Optimizing similar state handling
case ConnectionManagerState_Installcerts: // Install RootCA and Client certificates
case ConnectionManagerState_Installcerts_Dup: // Install new RootCA and Client certificates, to be registered into the EAP_TLS's <TEMPORARY CLONE> network
{
EapTls_Log("EapTls_RunConnectionManager::%s\n", duplicatingNetwork ? "ConnectionManagerState_Installcerts_Dup" : "ConnectionManagerState_Installcerts");
if (NULL != webApiResponse)
{
EapTlsConfig *network = duplicatingNetwork ? &radiusNetwork_dup : &radiusNetwork;
// Install the RootCA certificate, if we asked for one and if it's different
if (requestRootCaCertificate)
{
if (0 != EapTls_CompareCertificates((const char*)&network->eapTlsRootCertificate, webApiResponse->rootCACertficate, strlen((const char*)&network->eapTlsRootCertificate)))
{
iRes = EapTls_InstallRootCaCertificatePem((const char*)&network->eapTlsRootCertificate.id, webApiResponse->rootCACertficate);
if (EapTlsResult_Success != iRes)
{
iRes = EapTlsResult_FailedInstallingCertificates;
currentState = ConnectionManagerState_Error_Exit;
continue;
}
}
}
// Install the Client certificate, if we asked for one and if it's different
if (requestClientCertificate)
{
if (0 != EapTls_CompareCertificates((const char*)&network->eapTlsClientCertificate, webApiResponse->clientPublicCertificate, strlen((const char*)&network->eapTlsClientCertificate)))
{
// NOTE: it left to the customer to decide wither to use the private key password that "may" be returned from the WebAPI
// or a password that is hard-coded into the code (and that could be updated with future App updates)
iRes = EapTls_InstallClientCertificatePem((const char*)&network->eapTlsClientCertificate.id,
webApiResponse->clientPublicCertificate,
webApiResponse->clientPrivateKey,
#ifdef USE_CLIENT_CERT_PRIVATE_KEY_PASS_FROM_WEBAPI
webApiResponse->clientPrivateKeyPass);
#else
network->eapTlsClientCertificate.privateKeyPass);
#endif // USE_CLIENT_CERT_PRIVATE_KEY_PASS_FROM_WEBAPI
if (EapTlsResult_Success != iRes)
{
iRes = EapTlsResult_FailedInstallingCertificates;
currentState = ConnectionManagerState_Error_Exit;
continue;
}
}
}
}
else
{
iRes = EapTlsResult_FailedInstallingCertificates;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error: null WebAPI response\n");
continue;
}
// We now have the new certs installed, let's attempt to add the EAP-TLS network, or configure the duplicated one if we're coming from that path
currentState = duplicatingNetwork ? ConnectionManagerState_ConfigureEapTlsNetwork_Dup : ConnectionManagerState_AddEapTlsNetwork;
}
break;
// Optimizing similar state handling
case ConnectionManagerState_ConfigureEapTlsNetwork: // Configure the EAP_TLS's network security (installing CA/Client certs)
case ConnectionManagerState_ConfigureEapTlsNetwork_Dup: // Configure the EAP_TLS's <TEMPORARY CLONE> network security (installing CA/Client certs)
{
EapTls_Log("EapTls_RunConnectionManager::%s\n", duplicatingNetwork ? "ConnectionManagerState_ConfigureEapTlsNetwork_Dup" : "ConnectionManagerState_ConfigureEapTlsNetwork");
EapTlsConfig *network = duplicatingNetwork ? &radiusNetwork_dup : &radiusNetwork;
if (EapTls_ConfigureNetworkSecurity(network->eapTlsNetworkName, network->eapTlsClientIdentity, network->eapTlsRootCertificate.id, network->eapTlsClientCertificate.id) != EapTlsResult_Success)
{
EapTls_Log("Cannot configure network security for '%s'\n", network->eapTlsNetworkName);
// Certificates from the WebAPI are not valid --> return to the App
iRes = EapTlsResult_FailedConfiguringCertificates;
currentState = ConnectionManagerState_Error_Exit;
}
else
{
// Network is configured: let's connect
currentState = duplicatingNetwork ? ConnectionManagerState_ConnectToEapTlsNetwork_Dup : ConnectionManagerState_ConnectToEapTlsNetwork;
}
}
break;
case ConnectionManagerState_ConnectToEapTlsNetwork: // Attempt connecting to the EAP_TLS network
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_ConnectToEapTlsNetwork\n");
duplicatingNetwork = false; // reset the connection-attempt state
switch ((iRes = EapTls_ConnectToNetwork(radiusNetwork.eapTlsNetworkName)))
{
case EapTlsResult_Connecting:
{
// We just stay in this state until something happens, ultimately a timeout
}
break;
case EapTlsResult_Connected:
{
currentState = ConnectionManagerState_Connected_Exit;
EapTls_Log("Successfully connected to EAP-TLS network '%s'\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_FailedTargetingNetwork:
case EapTlsResult_FailedScanningNetwork:
{
// Timeout forcing a connection to the EAP-TLS network --> let's give back control to the app to decide
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Failed targeting EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_NetworkUnknown:
{
// The network is unknown --> this means that we might have a configuration issue (i.e. device relocated somewhere else)
// Let's contact the Web API for a full re-provisioning
requestRootCaCertificate = true;
requestClientCertificate = true;
currentState = ConnectionManagerState_RequestCertificates;
EapTls_Log("Unknown EAP-TLS network '%s' --> re-provisioning the device.\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_ConnectionError:
{
// Generic error connecting to the network --> let's give back control to the app to decide
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Connection error to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_AuthenticationError:
{
requestRootCaCertificate = true;
requestClientCertificate = true;
// We cannot connect to the network because of authentication failure --> let's request new certificates and test them on a temporary duplicated network
duplicatingNetwork = true;
currentState = ConnectionManagerState_RequestCertificates;
EapTls_Log("Error [%d] authenticating to EAP-TLS network '%s' --> requesting new certificates for a duplicated network configuration.\n", iRes, radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_AuthenticationError_InvalidRootCaCert:
{
requestRootCaCertificate = true;
requestClientCertificate = false;
// We cannot connect to the network for authentication failure --> let's request new certificates and test them on a temporary duplicated network
duplicatingNetwork = true;
currentState = ConnectionManagerState_RequestCertificates;
EapTls_Log("Error [%d] authenticating to EAP-TLS network '%s' --> requesting new certificates for a duplicated network configuration.\n", iRes, radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_AuthenticationError_InvalidClientCert:
case EapTlsResult_AuthenticationError_InvalidClientIdentity:
{
requestRootCaCertificate = false;
requestClientCertificate = true;
// We cannot connect to the network for authentication failure --> let's request new certificates and test them on a temporary duplicated network
duplicatingNetwork = true;
currentState = ConnectionManagerState_RequestCertificates;
EapTls_Log("Error [%d] authenticating to EAP-TLS network '%s' --> requesting new certificates for a duplicated network configuration.\n", iRes, radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_NetworkDisabled:
{
// For some reason, the network is disabled despite the target-scan EapTls_ConnectToNetwork. This may be due to other user threads concurrency --> let's give back control to the app to decide
//iRes = EapTlsResult_NetworkDisabled;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error handling connection to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_ConnectionTimeout:
{
// Timeout connecting to the network for technical reasons (as no diagnostics are available) --> let's give back control to the app to decide
//iRes = EapTlsResult_ConnectionTimeout;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Timeout connecting to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_FailedDiagnosingNetwork:
{
// For some reason, the network is disabled despite the target-scan EapTls_ConnectToNetwork. This may be due to other user threads concurrency --> let's give back control to the app to decide
//iRes = EapTlsResult_FailedDiagnosingNetwork;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error handling connection to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
}
break;
case EapTlsResult_Error:
{
// Failed initializing connection handler --> let's give back control to the app to decide
//iRes = EapTlsResult_Error;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Failed initializing connection handler to EAP-TLS network '%s' --> exiting\n", radiusNetwork.eapTlsNetworkName);
}
break;
default:
{
// DEV ERROR: should never get here, block execution to check call-stack.
currentState = ConnectionManagerState_TBD;
}
break;
}
}
break;
case ConnectionManagerState_ConnectToEapTlsNetwork_Dup: // Attempt connecting to the EAP_TLS's <TEMPORARY CLONE> network
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_ConnectToEapTlsNetwork_Dup\n");
switch ((iRes = EapTls_ConnectToNetwork(radiusNetwork_dup.eapTlsNetworkName)))
{
case EapTlsResult_Connecting:
{
// We just stay in this state until something happens, ultimately a timeout
}
break;
case EapTlsResult_Connected:
{
// Successfully connected to the new EAP_TLS network --> let's swap it with the original one
currentState = ConnectionManagerState_SwapEapTlsNetworks;
EapTls_Log("Successfully connected to the NEW EAP-TLS network '%s'\n", radiusNetwork_dup.eapTlsNetworkName);
}
break;
default:
{
// Any other error on this second attempt (on the duplicated network), is a total fail. Therefore the state-machine is reset back to the App's control.
// Failed connecting to the duplicated network --> let's give back control to the app to decide, with the last error in iRes
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Failed connecting to NEW EAP-TLS network '%s' --> exiting\n", radiusNetwork_dup.eapTlsNetworkName);
}
break;
}
}
break;
case ConnectionManagerState_SwapEapTlsNetworks: // Swap the original EAP_TLS network with the EAP_TLS's <TEMPORARY CLONE> network
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_SwapEapTlsNetworks\n");
// Delete the original network configuration
int networkId = WifiConfig_GetNetworkIdByConfigName(radiusNetwork.eapTlsNetworkName);
if (-1 == networkId)
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error looking-up network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork.eapTlsNetworkName, networkId, errno, strerror(errno));
}
else
{
// Delete the original network configuration
if (-1 == WifiConfig_ForgetNetworkById(networkId))
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error forgetting network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork.eapTlsNetworkName, networkId, errno, strerror(errno));
}
else
{
// Rename the temporary network with the original network configuration name
networkId = WifiConfig_GetNetworkIdByConfigName(radiusNetwork_dup.eapTlsNetworkName);
if (-1 == networkId)
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error looking-up network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsNetworkName, networkId, errno, strerror(errno));
}
else
{
// Rename the duplicated network to become the first configuration
int res = WifiConfig_SetConfigName(networkId, radiusNetwork.eapTlsNetworkName);
if (-1 == res)
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error renaming network '%s' - Id[%d]: errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsNetworkName, networkId, errno, strerror(errno));
}
else
{
if (EapTls_PersistNetworkConfig() == EapTlsResult_Success)
{
// Rename the NEW root & client certificates
res = CertStore_MoveCertificate(radiusNetwork_dup.eapTlsRootCertificate.id, radiusNetwork.eapTlsRootCertificate.id);
if (-1 == res)
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error renaming RootCA certificate '%s': errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsClientCertificate.id, errno, strerror(errno));
}
else
{
res = CertStore_MoveCertificate(radiusNetwork_dup.eapTlsClientCertificate.id, radiusNetwork.eapTlsClientCertificate.id);
if (-1 == res)
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Error renaming Client certificate '%s': errno=%d (%s) --> exiting\n", radiusNetwork_dup.eapTlsClientCertificate.id, errno, strerror(errno));
}
else
{
iRes = EapTlsResult_Connected;
currentState = ConnectionManagerState_Connected_Exit;
EapTls_Log("Successfully configured new '%s' EAP-TLS network!\n", radiusNetwork.eapTlsNetworkName);
}
}
}
else
{
iRes = EapTlsResult_FailedSwappingEapTlsNetworkConfig;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Cannot persist network configuration: errno=%d (%s) --> exiting\n", errno, strerror(errno));
}
}
}
}
}
}
break;
case ConnectionManagerState_ConnectToBootstrapNetwork: // Attempt connecting to a bootstrap network, in order to connect to the WebAPI and retrieve new certificates
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_ConnectToBootstrapNetwork\n");
iRes = EapTls_SetBootstrapNetworkEnabledState(eapTlsConfig, true);
if (EapTlsResult_Success == iRes)
{
if (NetworkInterfaceType_Ethernet == radiusNetwork.bootstrapNetworkInterfaceType)
{
// We're on Ethernet: let's directly call the WebAPI
EapTls_Log("Bootstrap network is on Ethernet: directly calling the WebAPI...\n");
iRes = EapTlsResult_Connected;
currentState = ConnectionManagerState_CallMdmWebApi;
}
else
{
EapTls_Log("Bootstrap network is on Wi-Fi: attempting to connect...\n");
switch ((iRes = EapTls_ConnectToNetwork(radiusNetwork.bootstrapNetworkName)))
{
case EapTlsResult_Connecting:
{
// We just stay in this state
}
break;
case EapTlsResult_Connected:
{
// Let's call the WebAPI
currentState = ConnectionManagerState_CallMdmWebApi;
}
break;
default:
{
// We cannot connect to the Bootstrap network --> let's give back control to the App to decide what to do next
iRes = EapTlsResult_FailedConnectingToBootstrapNetwork;
currentState = ConnectionManagerState_Error_Exit;
}
break;
}
}
}
else
{
// We cannot connect enable the Bootstrap network --> let's give back control to the App to decide what to do next
iRes = EapTlsResult_FailedConnectingToBootstrapNetwork;
currentState = ConnectionManagerState_Error_Exit;
}
}
break;
case ConnectionManagerState_RequestCertificates: // Initiate a new Client certificate request
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_RequestCertificates\n");
currentState = ConnectionManagerState_ConnectToBootstrapNetwork;
}
break;
case ConnectionManagerState_CallMdmWebApi: // Request a new Client certificate to the WebAPI/CMS
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_CallMdmWebApi\n");
iRes = EapTls_CallMdmWebApi(&radiusNetwork, requestRootCaCertificate, requestClientCertificate, &webApiResponseBlob);
if (EapTlsResult_Success == iRes)
{
// The WebAPI has successfully returned a response --> let's move to parsing
currentState = ConnectionManagerState_HandleMdmWebApiResponse;
}
else
{
iRes = EapTlsResult_FailedConnectingToMdmWebApi;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Failed calling WebAPI '%s' --> exiting\n", radiusNetwork.mdmWebApiInterfaceUrl);
}
}
break;
case ConnectionManagerState_HandleMdmWebApiResponse: // Handle the response from the WebAPI/CMS
{
EapTls_Log("EapTls_RunConnectionManager::ConnectionManagerState_HandleMdmWebApiResponse\n");
// The WebAPI "should" have returned new certificates in the webApiResponseBlock MemoryBlock
// The response will be a JSON in the following format:
// {
// "timestamp" : "2020-05-22T10:302:25.6828914+00:00",
// "rootCACertficate" : "<certificate in PEM format>",
// "eapTlsNetworkSsid" : "<the SId of the RADIUS network",
// "clientIdentity" : "<the client user identity>"
// "clientPublicCertificate" : "<certificate in PEM format>"
// "clientPrivateKey" : "<client's private in PEM format>"
// "clientPrivateKeyPass" : "<private key password>"
// }
free(webApiResponse);
webApiResponse = malloc(sizeof(WebApiResponse));
if (NULL != webApiResponse)
{
// Parse the WebAPI response and extract the optional RootCA certificate and the Client certificate
iRes = EapTls_ParseMdmWebApiResponse(&webApiResponseBlob, webApiResponse);
if (EapTlsResult_Success == iRes)
{
// Check if the Web API has returned what we asked for
if ((requestRootCaCertificate && 0 == *webApiResponse->rootCACertficate) || (requestClientCertificate && 0 == *webApiResponse->clientPublicCertificate))
{
iRes = EapTlsResult_FailedReceivingNewCertificates;
EapTls_Log("Failed receiving the requested certificates from then WebAPI '%s' (RootCA-%s, ClientCert=%s) --> exiting\n",
(requestRootCaCertificate && 0 == *webApiResponse->rootCACertficate) ? "OK" : "failed",
(requestClientCertificate && 0 == *webApiResponse->clientPublicCertificate) ? "OK" : "failed",
radiusNetwork.mdmWebApiInterfaceUrl);
}
else
{
// The WebAPI has returned new certificate(s) and stored them in the function-global webApiResponse variable
// Set and persist the assigned RADIUS network's SSID
strncpy(deviceConfiguration.eapTlsNetworkSsid, webApiResponse->eapTlsNetworkSsid, sizeof(deviceConfiguration.eapTlsNetworkSsid) - 1);
if (EapTlsResult_Success == EapTls_StoreDeviceConfiguration(&deviceConfiguration))
{
strncpy(radiusNetwork.eapTlsNetworkSsid, deviceConfiguration.eapTlsNetworkSsid, sizeof(radiusNetwork.eapTlsNetworkSsid) - 1);
}
if (requestClientCertificate)
{
// The WebAPI has returned a new client certificate, so let's set & persist its related client's identity
strncpy(deviceConfiguration.eapTlsClientIdentity, webApiResponse->clientIdentity, sizeof(deviceConfiguration.eapTlsClientIdentity) - 1);
if (EapTlsResult_Success == EapTls_StoreDeviceConfiguration(&deviceConfiguration))
{
strncpy(radiusNetwork.eapTlsClientIdentity, deviceConfiguration.eapTlsClientIdentity, sizeof(radiusNetwork.eapTlsClientIdentity) - 1);
}
}
// Now split paths: differentiate if we're coming from the first (failed) attempt, or if this is just the first EAP_TLS installation
if (duplicatingNetwork)
{
// We first clone the network, then we install the certificates from that state
currentState = ConnectionManagerState_CloneEapTlsNetwork;
}
else
{
currentState = ConnectionManagerState_Installcerts;
}
}
}
else
{
iRes = EapTlsResult_FailedParsingMdmWebApiResponse;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Failed parsing response from WebAPI '%s' --> exiting\n", radiusNetwork.mdmWebApiInterfaceUrl);
}
}
else
{
iRes = EapTlsResult_FailedParsingMdmWebApiResponse;
currentState = ConnectionManagerState_Error_Exit;
EapTls_Log("Out of memory parsing response from WebAPI '%s' --> exiting\n", radiusNetwork.mdmWebApiInterfaceUrl);
}
free(webApiResponseBlob.data);
webApiResponseBlob.data = NULL;
}
break;
case ConnectionManagerState_Connected_Exit: // Successfully connected to the EAP-TLS network -> the App can proceed its execution
{
EapTls_Log("EapTls_RunConnectionManager::EAP_TLS_CONNECTED_EXIT - result=%d\n", iRes);
exitStateMachine = true;
}
break;
case ConnectionManagerState_Error_Exit: // FAILED to connect to the EAP-TLS network -> let's return so the App can decide the next steps
{
EapTls_Log("EapTls_RunConnectionManager::EAP_TLS_ERROR_EXIT - result=%d\n", iRes);
exitStateMachine = true;
}
break;
default:
EapTls_Log("EapTls_RunConnectionManager::UNDEFINED STATE '%d'!\n", currentState);
currentState = ConnectionManagerState_TBD;
break;
}
}
if (NULL != webApiResponse)
{
free(webApiResponse);
}
return iRes;
}